package base;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;

import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

import base.engine.InputHandler;
import base.engine.RenderHandler;
import base.engine.UpdateThread;
import base.engine.WorldHandler;

public class Start
{
	private static final int WIN_WIDTH = 160;
	private static final int WIN_HEIGHT = WIN_WIDTH/12*9;
	private static final int WIN_SCALE = 3;

	public static final String VERSION = "0.0.1";
	public static final String TITLE = "Project Hg v" + VERSION;

	public static final int FPS_MAX = 60;

	public static boolean isDebugMode = true;

	public static RenderHandler renderEngine;
	public static InputHandler inputManager;
	public static WorldHandler worldManager;
	public static boolean expireManager;

	private static Thread updateThread;
	private static boolean running = false;

	public static void main(String[] args)
	{
		try
		{
			startApplication();
			runApplication();
		}
		catch (Exception e)
		{
			e.printStackTrace(System.err);
			Sys.alert(TITLE, "An error occured and will now exit.");
		}
		finally
		{
			try
			{
				Thread.sleep(100);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			trashApplication();
		}
	}

	private static void startApplication()
	{
		running = true;

		initWindow();
		initGL();
		initInput();

		updateThread = new Thread(new UpdateThread());

		renderEngine = new RenderHandler();
		inputManager = new InputHandler();
		worldManager = new WorldHandler();
	}

	private static void runApplication()
	{
		renderEngine.initiate();
		inputManager.initiate();
		worldManager.initiate();

		updateThread.start();

		while(running)
		{
			if (Display.isCloseRequested())
			{
				Start.terminateApplication();
				Start.cleanUpApplication();
				return;
			}
			else if (UpdateThread.shouldScreenUpdate)
			{
				Display.update();

				if (!expireManager)
				{
					renderApplication();
				}
				else
				{
					cleanUpApplication();
				}

				UpdateThread.shouldScreenUpdate = false;
			}

			try
			{
				Thread.sleep(2);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}

	private static void cleanUpApplication()
	{
		try
		{
			Thread.sleep(10);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}

		worldManager.terminate();
		inputManager.terminate();
		renderEngine.terminate();

		expireManager = false;

		if (running)
		{
			renderEngine.initiate();
			inputManager.initiate();
			worldManager.initiate();
		}
	}

	private static void trashApplication()
	{
		Display.destroy();
		Keyboard.destroy();
		Mouse.destroy();
		System.exit(0);
	}

	/**Ends the application next tick*/
	public static void terminateApplication()
	{
		running = false;
		expireManager = true;
	}

	/**Refresh the application next tick*/
	public static void refreshApplication()
	{
		expireManager = true;
	}

	/**Returns true if application is still running*/
	public static boolean isRunning()
	{
		return running;
	}

	protected static void renderApplication()
	{
		glClear(GL_COLOR_BUFFER_BIT);
		glLoadIdentity();

		renderEngine.update();

		Display.sync(FPS_MAX);
	}

	private static void initWindow()
	{
		try
		{
			Display.setDisplayMode(new DisplayMode(WIN_WIDTH * WIN_SCALE, WIN_HEIGHT * WIN_SCALE));
			Display.setTitle(Start.TITLE);
			Display.setVSyncEnabled(true);

			Display.create();
		}
		catch (LWJGLException e)
		{
			e.printStackTrace();
		}
	}

	private static void initGL()
	{
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, Display.getWidth(), 0, Display.getHeight(), -1, 1);
		glMatrixMode(GL_MODELVIEW);

		glDisable(GL_DEPTH_TEST);
		glClearColor(0,0,0,0);
	}

	private static void initInput()
	{
		try
		{
			Keyboard.create();
			Mouse.create();
		}
		catch (LWJGLException e)
		{
			e.printStackTrace();
		}
	}
}
